package cn.bearspark.article.provider.dao.repository;

import cn.bearspark.article.provider.dao.po.article.ArticleRepPO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 专栏的 标题、摘要、内容 数据的数据仓库对象
 *
 * @author f
 */
@Repository
public class ArticleRepository {

    /**
     * 专栏 id 字段的名称
     */
    private static final String ID_FILED_NAME = "articleId";

    /**
     * 专栏的索引名称
     */
    private static final String INDEX = "article";

    /**
     * 包含专栏 标题、内容 的字段名称数组
     */
    private static final String[] TC_INCLUDES = {"title", "content"};

    /**
     * 包含专栏 标题、摘要 的字段名称数组
     */
    private static final String[] TS_INCLUDES = {"title", "summary"};

    /**
     * 排除空的字段名称数组
     */
    private static final String[] EMPTY_EXCLUDES = {};

    private final RestHighLevelClient restHighLevelClient;

    public ArticleRepository(RestHighLevelClient restHighLevelClient) {
        this.restHighLevelClient = restHighLevelClient;
    }

    /**
     * 保存专栏 标题、摘要、内容 数据
     *
     * @param articleRepPO 专栏 标题、摘要、内容 数据对象
     */
    public void save(ArticleRepPO articleRepPO) {
        IndexRequest indexRequest = new IndexRequest(INDEX);
        indexRequest.id(String.valueOf(articleRepPO.getArticleId()));
        indexRequest.source(getSourceMap(articleRepPO));

        try {
            restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            // 解决无法解析的问题
            if (e.getMessage().endsWith("201 Created}")) {
                return;
            }
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除指定专栏 标题、摘要、内容 数据
     *
     * @param articleId 待删除的专栏 id
     */
    public void deleteById(long articleId) {
        DeleteRequest deleteRequest = new DeleteRequest(INDEX, String.valueOf(articleId));
        try {
            restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            // 解决无法解析的问题
            if (e.getMessage().endsWith("200 OK}")) {
                return;
            }
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取指定专栏的 标题、摘要、内容 数据
     *
     * @param articleId 指定的专栏 id
     * @return 指定专栏的 标题、摘要、内容 数据
     */
    public ArticleRepPO getById(long articleId) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
                .query(QueryBuilders.idsQuery().addIds(String.valueOf(articleId)));
        SearchRequest searchRequest = new SearchRequest()
                .indices(INDEX)
                .source(sourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 根据 id 查询的结果只有一个
            return convertFromHit(searchResponse.getHits().getHits()[0]);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取指定专栏的 标题、内容 数据
     *
     * @param articleId 指定的专栏 id
     * @return 指定专栏的 标题、内容 数据
     */
    public ArticleRepPO getTCById(long articleId) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
                .query(QueryBuilders.idsQuery().addIds(String.valueOf(articleId)))
                .fetchSource(TC_INCLUDES, EMPTY_EXCLUDES);
        SearchRequest searchRequest = new SearchRequest()
                .indices(INDEX)
                .source(sourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 根据 id 查询的结果只有一个
            return convertFromHit(searchResponse.getHits().getHits()[0]);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 映射指定专栏的 标题、摘要 数据
     *
     * @param articleIdList 指定的专栏 id 集合
     * @return 指定专栏的 标题、摘要 数据映射，key：专栏 id，value：专栏的 标题、摘要 数据
     */
    public Map<Long, ArticleRepPO> mapTS(List<Long> articleIdList) {
        IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery()
                .addIds(articleIdList.stream().map(String::valueOf).toArray(String[]::new));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
                .query(idsQueryBuilder)
                .fetchSource(TS_INCLUDES, EMPTY_EXCLUDES);
        SearchRequest searchRequest = new SearchRequest()
                .indices(INDEX)
                .source(sourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            Map<Long, ArticleRepPO> articleRepPOMap = new HashMap<>(hits.getHits().length);
            for (SearchHit hit : hits) {
                articleRepPOMap.put(Long.valueOf(hit.getId()), convertFromHit(hit));
            }
            return articleRepPOMap;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 从 SearchHit 对象转换成 ArticleRepPO 对象
    private ArticleRepPO convertFromHit(SearchHit hit) {
        ArticleRepPO tagPO = BeanUtil.mapToBean(hit.getSourceAsMap(), ArticleRepPO.class, false, CopyOptions.create());
        tagPO.setArticleId(Long.valueOf(hit.getId()));
        return tagPO;
    }

    /**
     * 修改专栏 标题、摘要、内容 数据
     *
     * @param articleRepPO 专栏 标题、摘要、内容 数据对象
     */
    public void update(ArticleRepPO articleRepPO) {
        UpdateRequest updateRequest = new UpdateRequest()
                .index(INDEX)
                .id(String.valueOf(articleRepPO.getArticleId()))
                .doc(getSourceMap(articleRepPO));
        try {
            restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            // 解决无法解析的问题
            if (e.getMessage().endsWith("200 OK}")) {
                return;
            }
            throw new RuntimeException(e);
        }
    }

    // 从 ArticleRepPO 对象转换成 Map<String, Object> 映射
    private Map<String, Object> getSourceMap(ArticleRepPO articleRepPO) {
        Map<String, Object> source = BeanUtil.beanToMap(articleRepPO);
        source.remove(ID_FILED_NAME);
        return source;
    }
}
